package com.example.mq.mqClient;

import com.example.mq.common.BasicArguments;
import com.example.mq.common.BasicReturns;
import com.example.mq.common.Consumer;
import com.example.mq.common.Request;
import com.example.mq.common.basic.*;
import com.example.mq.mqServer.BrokerServer;
import com.example.mq.mqServer.core.exchange.ExchangeType;
import com.example.mq.mqServer.core.message.BasicProperties;
import com.example.mq.mqServer.exception.MqException;
import com.example.mq.mqServer.util.SerializableUtils;
import com.sun.xml.internal.bind.v2.TODO;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.context.PayloadApplicationEvent;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @BelongsProject: 0.mq
 * @BelongsPackage: com.example.mq.mqClient
 * @Author : 游在海里的鱼
 * @CreateTime : 2024-01-23  09:54
 * @Version : 1.0
 * @ClassName :频道
 * @Description :
 */

@Data
@Slf4j
public class Channel {
    //频道标识符
    private String channelId;
    //所属TCP连接
    private Connection connection;
    //服务端的响应哈希表,客户端发送请求后的所有响应放到这个哈希表中
    private ConcurrentHashMap<String, BasicReturns> basicReturnsMap = new ConcurrentHashMap<>();
    //回调函数,每个channel都有一个唯一的回调函数
    private Consumer consumer =null;

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 构造方法
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public Channel(String channelId, Connection connection) {
        this.channelId = channelId;
        this.connection = connection;
    }

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 创建频道
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean createChannel() {
        try {
            //1.构建请求的的属性
            BasicArguments basicArguments = new BasicArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //2.构建请求，类型为0x1，表示创建channel
            Request request = new Request();
            request.setType(0x1);
            request.setLength(payload.length);
            request.setPayload(payload);

            //3.发送请求
            connection.writeRequest(request);

            //4.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[createChannel] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
        return false;
    }


    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 删除频道
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean deleteChannel(){
        try {
            //1.构建请求的的属性
            BasicArguments basicArguments = new BasicArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //2.构建请求，类型为0x2，表示删除channel
            Request request = new Request();
            request.setType(0x2);
            request.setLength(payload.length);
            request.setPayload(payload);

            //3.发送请求
            connection.writeRequest(request);

            //4.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[deleteChannel] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
        return false;
    }

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 声明交换机
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean exchangeDeclare(String exchangeName, ExchangeType exchangeType, boolean durable, boolean autoDelete, Map<String,Object> arguments){
        try {
            //1.构建请求的的属性
            ExchangeDeclareArguments basicArguments = new ExchangeDeclareArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            basicArguments.setExchangeName(exchangeName);
            basicArguments.setExchangeType(exchangeType);
            basicArguments.setDurable(durable);
            basicArguments.setAutoDelete(autoDelete);
            basicArguments.setArguments(arguments);
            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //2.构建请求，类型为0x3，表示创建exchange
            Request request = new Request();
            request.setType(0x3);
            request.setLength(payload.length);
            request.setPayload(payload);

            //3.发送请求
            connection.writeRequest(request);

            //4.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[exchangeDeclare] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
        return false;
    }

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 删除交换机
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean exchangeDelete(String exchangeName){
        try {
            //1.构建请求的的属性
            ExchangeDeleteArguments basicArguments = new ExchangeDeleteArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            basicArguments.setExchangeName(exchangeName);

            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //2.构建请求，类型为0x4，表示删除exchange
            Request request = new Request();
            request.setType(0x4);
            request.setLength(payload.length);
            request.setPayload(payload);

            //3.发送请求
            connection.writeRequest(request);

            //4.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[exchangeDelete] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
        return false;
    }

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 创建队列
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean queueDeclare(String queueName, boolean durable, boolean exclusive,
                                boolean autoDelete, Map<String,Object> arguments){
        try {
            //1.构建请求的的属性
            QueueDeclareArguments basicArguments = new QueueDeclareArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            basicArguments.setQueueName(queueName);
            basicArguments.setDurable(durable);
            basicArguments.setExclusive(exclusive);
            basicArguments.setAutoDelete(autoDelete);
            basicArguments.setArguments(arguments);

            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //2.构建请求，类型为0x5，表示创建queue
            Request request = new Request();
            request.setType(0x5);
            request.setLength(payload.length);
            request.setPayload(payload);

            //3.发送请求
            connection.writeRequest(request);

            //4.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[queueDeclare] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
        return false;
    }

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 删除队列
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean queueDelete(String queueName){
        try {
            //1.构建请求的的属性
            QueueDeleteArguments basicArguments = new QueueDeleteArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            basicArguments.setQueueName(queueName);

            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //2.构建请求，类型为0x6，表示删除queue
            Request request = new Request();
            request.setType(0x6);
            request.setLength(payload.length);
            request.setPayload(payload);

            //3.发送请求
            connection.writeRequest(request);

            //4.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[queueDelete] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
        return false;
    }

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 新增绑定
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean queueBind(String exchangeName,String queueName,String bindingKey){
        try{
            //1.构建请求的的属性
            QueueBindingArguments basicArguments = new QueueBindingArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            basicArguments.setExchangeName(exchangeName);
            basicArguments.setQueueName(queueName);
            basicArguments.setBindingKey(bindingKey);

            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //2.构建请求，类型为0x7，表示新增绑定
            Request request = new Request();
            request.setType(0x7);
            request.setLength(payload.length);
            request.setPayload(payload);

            //3.发送请求
            connection.writeRequest(request);

            //4.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[queueBind] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
            return false;
    }

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 删除绑定
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean  queueUnBind(String exchangeName,String queueName){
        try{
            //1.构建请求的的属性
            QueueUnBindingArguments basicArguments = new QueueUnBindingArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            basicArguments.setExchangeName(exchangeName);
            basicArguments.setQueueName(queueName);

            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //2.构建请求，类型为0x8，表示删除绑定
            Request request = new Request();
            request.setType(0x8);
            request.setLength(payload.length);
            request.setPayload(payload);

            //3.发送请求
            connection.writeRequest(request);

            //4.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[queueUnBind] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
        return false;
    }


    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 发送消息
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean basicPublish(String exchangeName, String routingKey,
                                BasicProperties basicProperties, byte[] data){
        try{
            //1.构建请求的的属性
            BasicPublishArguments basicArguments = new BasicPublishArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            basicArguments.setExchangeName(exchangeName);
            basicArguments.setRoutingKey(routingKey);
            basicArguments.setBasicProperties(basicProperties);
            basicArguments.setData(data);

            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //2.构建请求，类型为0x9，表示发送消息
            Request request = new Request();
            request.setType(0x9);
            request.setLength(payload.length);
            request.setPayload(payload);

            //3.发送请求
            connection.writeRequest(request);

            //4.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[basicPublish] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
        return false;
    }

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 订阅消息
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean basicConsume(String consumerTag, String queueName, boolean autoAck, Consumer consumer){
        try{
            //1.定义订阅消息后的回调函数
            if(this.consumer != null){
                new MqException("[Channel] [basicConsume] [channel:"+channelId+"]已有回调函数").printStackTrace();
                return false;
            }
            this.consumer = consumer;

            //2.构建请求的的属性
            BasicConsumeArguments basicArguments = new BasicConsumeArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            basicArguments.setConsumerTag(consumerTag);
            basicArguments.setQueueName(queueName);
            basicArguments.setAutoAck(autoAck);

            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //3.构建请求，类型为0xa，表示订阅消息
            Request request = new Request();
            request.setType(0xa);
            request.setLength(payload.length);
            request.setPayload(payload);

            //4.发送请求
            connection.writeRequest(request);

            //5.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[basicConsume] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
        return false;
    }


    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 消息应答
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean basicAck(String queueName,String messageId){
        try{
            //1.构建请求的的属性
            BasicAckArguments basicArguments = new BasicAckArguments();
            basicArguments.setChannelId(channelId);
            basicArguments.setRid(generateRid());
            basicArguments.setQueueName(queueName);
            basicArguments.setMessageId(messageId);

            byte[] payload = SerializableUtils.toBytes(basicArguments);

            //2.构建请求，类型为0xb，表示消息应答
            Request request = new Request();
            request.setType(0xb);
            request.setLength(payload.length);
            request.setPayload(payload);

            //3.发送请求
            connection.writeRequest(request);

            //4.等待服务端的响应
            BasicReturns basicReturns = waitResult(basicArguments.getRid());

            return basicReturns.isRet();
        } catch (IOException e) {
            log.error("[basicAck] 将参数转成字节数组时出错");
            e.printStackTrace();
        }
        return false;
    }

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 等待消息响应
     * @Describe: 将响应放入响应哈希表，并唤醒等待响应
     * @modifier: [public] [实例方法]
     **/
    public void putReturn(BasicReturns basicReturns) {
        basicReturnsMap.put(basicReturns.getRid(),basicReturns);
        synchronized (this){
            notifyAll();
        }
    }

    /* ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 等待响应
     * @Describe: TODO
     * @modifier: [private] [实例方法]
    **/
    private BasicReturns waitResult(String rid) {
        //1.循环等待响应
        while(basicReturnsMap.get(rid) == null){
            //加锁
            synchronized (this) {
                //等待
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        //2.获取响应，用于返回
        BasicReturns basicReturns = basicReturnsMap.get(rid);
        //3.从响应图中删除此响应
        basicReturnsMap.remove(rid);
        //4.返回响应
        return basicReturns;
    }

    /* ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 生成一个Rid，使用UUID
     * @Describe: TODO
     * @modifier: [private] [实例方法]
    **/
    private String generateRid(){
        return "R-" + UUID.randomUUID();
    }


}
